using ScottPlot;
using ScottPlot.Avalonia;
using ScottPlot.Plottables;

namespace Digitalmes.AvaloniaUI.ViewModels;

public sealed partial class PlotDemoViewModel : ViewModelBase, IDisposable
{
    private readonly CancellationTokenSource _cts = new();

    public AvaPlot PlotControl1 { get; }

    public AvaPlot PlotControl2 { get; }

    public AvaPlot PlotControl3 { get; }

    public AvaPlot PlotControl4 { get; }

    public AvaPlot PlotControl5 { get; }

    public AvaPlot PlotControl6 { get; }

    public PlotDemoViewModel()
    {
        // 中文字体，解决中文显示乱码问题
        //var plotFamilyName = SkiaSharp.SKFontManager.Default.MatchCharacter('汉').FamilyName;
        //plot.Font.Set(plotFamilyName);

        PlotControl1 = new AvaPlot();
        PlotControl2 = new AvaPlot();
        PlotControl3 = new AvaPlot();
        PlotControl4 = new AvaPlot();
        PlotControl5 = new AvaPlot();
        PlotControl6 = new AvaPlot();

        Plot1();
        Plot2();
        Plot3();
        Plot4();
        Plot5();
        Plot6();
    }

    void Plot1()
    {
        var avaPlot = PlotControl1;
        int count = 99;

        // AvaPlot
        int[] dataX = new int[count];
        double[] dataY1 = new double[count];
        double[] dataY2 = new double[count];

        Random rnd = new();

        for (int i = 0; i < count; i++)
        {
            dataX[i] = i;
            dataY1[i] = i + rnd.NextDouble() * 50;
            dataY2[i] = i + rnd.NextDouble() * 20;
        }

        // plot the data
        avaPlot.Plot.Add.Scatter(dataX, dataY1);
        avaPlot.Plot.Add.Scatter(dataX, dataY2);

        // signal plots are optimized for displaying evenly spaced data
        // 信号图经过优化，可显示均匀间隔的数据
        //avaPlot.Plot.Add.Signal(dataY1);
        //avaPlot.Plot.Add.Signal(dataY2);

        // customize the axis labels
        avaPlot.Plot.Title("ScottPlot Line");
        avaPlot.Plot.XLabel("Horizontal Axis");
        avaPlot.Plot.YLabel("Vertical Axis");
        avaPlot.Plot.Font.Automatic(); // 自动使用最优的字体，解决中文显示乱码问题（在设置 title、label 后才有效）

        //avaPlot.Plot.Axes.SetLimits(-100, 150, -5, 5); // set limits that do not fit the data
        //avaPlot.Plot.Axes.AutoScale(); // reset limits to fit the data（自适应数据）
    }

    void Plot2()
    {
        var avaPlot = PlotControl2;

        // create a bar plot
        double[] values = { 5, 10, 7, 13, 25, 60 };
        avaPlot.Plot.Add.Bars(values);
        avaPlot.Plot.Axes.Margins(bottom: 0);

        // create a tick for each bar
        Tick[] ticks =
        [
            new(0, "First Long Title"),
            new(1, "Second Long Title"),
            new(2, "Third Long Title"),
            new(3, "Fourth Long Title"),
            new(4, "Fifth Long Title"),
            new(5, "Sixth Long Title")
        ];
        avaPlot.Plot.Axes.Bottom.TickGenerator = new ScottPlot.TickGenerators.NumericManual(ticks);
        avaPlot.Plot.Axes.Bottom.TickLabelStyle.Rotation = 45;
        avaPlot.Plot.Axes.Bottom.TickLabelStyle.Alignment = Alignment.MiddleLeft;
        avaPlot.Plot.Font.Automatic();
    }

    void Plot3()
    {
        var avaPlot = PlotControl3;

        ScottPlot.DataGenerators.RandomWalker walker = new(0, multiplier: 0.01);
        var logger1 = avaPlot.Plot.Add.DataLogger();
        logger1.ViewSlide();
        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(10).ConfigureAwait(false);
                logger1.Add(walker.Next());
            }
        });

        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(50).ConfigureAwait(false);

                if (logger1.HasNewData)
                {
                    avaPlot.Refresh();
                }
            }
        });
    }

    void Plot4()
    {
        var avaPlot = PlotControl4;

        ScottPlot.DataGenerators.RandomWalker walker1 = new(0, multiplier: 0.01);
        ScottPlot.DataGenerators.RandomWalker walker2 = new(1, multiplier: 0.01);
        var logger1 = avaPlot.Plot.Add.DataLogger();
        var logger2 = avaPlot.Plot.Add.DataLogger();
        logger1.ViewJump();
        logger2.ViewJump();

        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(10).ConfigureAwait(false);

                logger1.Add(walker1.Next());
                logger2.Add(walker2.Next());
            }
        });

        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(50).ConfigureAwait(false);

                if (logger1.HasNewData || logger2.HasNewData)
                {
                    avaPlot.Refresh();
                }
            }
        });
    }

    void Plot5()
    {
        var avaPlot = PlotControl5;

        ScottPlot.DataGenerators.RandomWalker walker1 = new(0);
        ScottPlot.DataGenerators.RandomWalker walker2 = new(1);
        var stream1 = avaPlot.Plot.Add.DataStreamer(1000);
        var stream2 = avaPlot.Plot.Add.DataStreamer(1000);
        stream1.ViewScrollLeft();
        stream2.ViewScrollLeft();

        // disable mouse interaction by default
        avaPlot.UserInputProcessor.Disable();

        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(10).ConfigureAwait(false);
                
                stream1.Add(walker1.Next());
                stream2.Add(walker2.Next());
            }
        });

        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(50).ConfigureAwait(false);

                if (stream1.HasNewData || stream2.HasNewData)
                {
                    avaPlot.Refresh();
                }
            }
        });
    }

    void Plot6()
    {
        var avaPlot = PlotControl6;

        ScottPlot.DataGenerators.RandomWalker walker1 = new(0);
        ScottPlot.DataGenerators.RandomWalker walker2 = new(1);
        var stream1 = avaPlot.Plot.Add.DataStreamer(1000);
        var stream2 = avaPlot.Plot.Add.DataStreamer(1000);
        stream1.ViewWipeRight(0.1);
        stream2.ViewWipeRight(0.1);

        var vLine = avaPlot.Plot.Add.VerticalLine(0, 2, Colors.Red);

        // disable mouse interaction by default
        avaPlot.UserInputProcessor.Disable();

        Task.Run(async () =>
        {
            int count = 5;

            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(10).ConfigureAwait(false);

                stream1.AddRange(walker1.Next(count));
                stream2.AddRange(walker2.Next(count));

                // slide marker to the left
                avaPlot.Plot.GetPlottables<Marker>()
                    .ToList()
                    .ForEach(m => m.X -= count);

                // remove off-screen marks
                avaPlot.Plot.GetPlottables<Marker>()
                    .Where(m => m.X < 0)
                    .ToList()
                    .ForEach(m => avaPlot.Plot.Remove(m));
            }
        });

        Task.Run(async () =>
        {
            while (!_cts.IsCancellationRequested)
            {
                await Task.Delay(50).ConfigureAwait(false);
                if (stream1.HasNewData)
                {
                    vLine.IsVisible = stream1.Renderer is ScottPlot.DataViews.Wipe;
                    vLine.Position = stream1.Data.NextIndex * stream1.Data.SamplePeriod + stream1.Data.OffsetX;

                    avaPlot.Refresh();
                }
            }
        });
    }

    protected override void Dispose(bool disposing)
    {
        _cts.Cancel();
        base.Dispose(disposing);
    }
}
